/*
 * ﻿Copyright (C) 2009-2013 NewMain Softech
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package com.newmainsoftech.gwetons.shared.logging;

import com.google.gwt.core.client.GWT;
import com.newmainsoftech.gwetons.client.logging.JsniLoggerWrapFactoryImpl;
import com.newmainsoftech.gwetons.client.logging.LoggerWrapGinModule;
import com.newmainsoftech.gwetons.client.logging.LoggerWrapGinjector;

/**
 * Factory for {@link LoggerWrap} instance. <br />
 * LoggerWrap is the framework for GWT client-side code and server-side code 
 * to enable dynamically switching logger framework/configuration via the next features: 
 * <ul>
 * <li>Wrapper of logger</li>
 * <li>Injection mechanism of logger</li>
 * </ul>
 * Thereby, it allows to keep the integrity of the usage style of logger among GWT client-side code 
 * and server-side code even for different configurations.<br />
 * LoggerWrap framework may suit you when you like:
 * <ul>
 * <li>To have the identical logging style for between client side (GWT) and server side.</li>
 * <li>To use other logging framework than JUL (<code>java.util.logging</code>) package.</li>
 * <li>To have ability of dynamically switching between different logging implementation or frameworks.</li>
 * </ul>
 * <code>LoggerWrapFactoryImpl</code> is the factory class for both of client side and server side. 
 * You may use {@link LoggerWrapFactoryInstanceHolder} class to get the default instance of this 
 * class for your convenience.<br />
 * For logging inside of JSNI method at client side, use {@link JsniLoggerWrapFactoryImpl} factory class 
 * instead.
 * 
 * <h4>Usage example</h4>
 * <ul>
 * <li><b>Logger consumer side</b> - To obtain <code>LoggerWrap</code> instance<br />
 * <ul>
 * <li>If your <b>class does not have static method to be logged</b>, then you <b>may extend your class 
 * from {@link LoggerWrapAdapterTemplate} abstract class</b>.
 * <ol>
 * <li>Override {@link LoggerWrapAdapterTemplate#getNameForLogging()} abstract method to return 
 * the name of your logger.
 * </li>
 * <li>Get <code>LoggerWrap</code> instance via {@link LoggerWrapAdapterTemplate#getLoggerWrap()}
 * method, and use it to record log in your method.
 * </li>
 * </ol>
 * </li>
 * <li>When your <b>class has static method to be logged or cannot be extended from
 * <code>LoggerWrapAdapterTemplate</code> abstract class</b>.<br />
 * Use either one of following getter method for <code>LoggerWrap</code> instance:
 * <ul>
 * <li> Overloaded static getLoggerWrap methods of {@link LoggerWrapFactoryImpl} class
 * </li>
 * <li>{@link #getLoggerWrap(String)} method of <code>LoggerWrapFactoryImpl</code> instance as default 
 * implementation of {@link LoggerWrapFactory} interface.
 * </li>
 * <li>{@link LoggerWrapFactory#getLoggerWrap(String)} method of custom {@link LoggerWrapFactory} 
 * implementation class instance.
 * </li>
 * </ul>
 * </li>
 * </ul>
 * </li>
 * <li><b>Logger provider side</b> - To configure framework to yield instance of custom
 * <code>LoggerWrap</code> implementation<br />
 * Change default injector being given by <code>LoggerWrapFactoryInstanceHolder</code> class. <br />
 * <code>LoggerWrapFactoryInstanceHolder</code> class holds the default <code>LoggerWrapFactory</code> 
 * instance (what is a <code>LoggerWrapFactoryImpl</code> instance). Create {@link LoggerWrapInjector}
 * implementation class to yield your <code>LoggerWrap</code> implementation instance and set it
 * via {@link #setLoggerWrapInjector(LoggerWrapInjector)} of that default 
 * <code>LoggerWrapFactory</code> instance. <br />
 * That makes all objects (logger consumers), what are obtaining logger via either default configuration of
 * <code>LoggerWrapAdapterTemplate</code> or <code>LoggerWrapFactory</code> instance obtained by
 * {@link LoggerWrapFactoryInstanceHolder#getLoggerWrapFactory()} method, use your custom
 * <code>LoggerWrap</code> implementation.<br />
 * So, it is possible to dynamically change the type of logger in multiple logger consumer instances 
 * at once.
 * </li>
 * <li>Direct and more granular control way<br />
 * Of course, you may use the following ways too.
 * <ul>
 * <li>Create custom <code>LoggerWrapFactory</code> implementation what yields your custom
 * <code>LoggerWrap</code> implementation and set its instance among your logger consumer
 * instances.
 * </li>
 * <li>Create custom <code>LoggerWrap</code> implementation and set its instance directly
 * to each of your logger consumer instances.
 * </li>
 * </ul>
 * </li>
 * </ul>
 * 
 * @author <a href="mailto:artymt@gmail.com">Arata Y.</a>
 */
public class LoggerWrapFactoryImpl implements LoggerWrapFactory {
	private static LoggerWrapInjector loggerWrapInjector;
		public synchronized static LoggerWrapInjector getLoggerWrapInjector() {
			if ( LoggerWrapFactoryImpl.loggerWrapInjector == null) {
				if ( GWT.isClient()) {
					LoggerWrapGinjector loggerWrapGinjector = GWT.create( LoggerWrapGinjector.class);
					LoggerWrapFactoryImpl.setLoggerWrapInjector( loggerWrapGinjector);
				}
				// GWT compiler throws error when code of setting LoggerWrapInjectorImpl instance for 
				// non-JavaScript case is here; therefore, for non-JavaScript case, LoggerWrapInjector 
				// instance needs to be manually set.
//TODO I may be able to work around GWT compiler error by using Java Agent. 
			}
			return LoggerWrapFactoryImpl.loggerWrapInjector;
		}
		
		/**
		 * Set {@link LoggerWrapInjector} instance to {@link #loggerWrapInjector} static member 
		 * field what will be used to generate {@link LoggerWrap} instance via 
		 * {@link #getLoggerWrap(String)} method. <br />
		 * When no {@link LoggerWrapInjector} instance has not been set via this method, 
		 * for client side, {@link LoggerWrapGinModule} class will be used as default. However, for server 
		 * side, <code>LoggerWrapInjectorContract</code> instance must have been set via this method 
		 * before use of this class.
		 * 
		 * @param loggerWrapInjector
		 */
		public synchronized static void setLoggerWrapInjector( LoggerWrapInjector loggerWrapInjector) {
			LoggerWrapFactoryImpl.loggerWrapInjector = loggerWrapInjector;
			LoggerWrapFactoryImpl.setLoggerWrapFactoryLoggerWrap( null);
		}
	
	// Logging objects for LoggerWrapFactory itself -----------------------------------------------
	private static LoggerWrap loggerWrapFactoryLoggerWrap;
		public synchronized static LoggerWrap getLoggerWrapFactoryLoggerWrap() {
			if ( LoggerWrapFactoryImpl.loggerWrapFactoryLoggerWrap == null) {
				LoggerWrap loggerWrapObj = LoggerWrapFactoryImpl.getLoggerWrapInjector().getLoggerWrap();
					loggerWrapObj.setUpLogger( LoggerWrapFactoryImpl.class.getName());
				LoggerWrapFactoryImpl.setLoggerWrapFactoryLoggerWrap( loggerWrapObj);
			}
			return LoggerWrapFactoryImpl.loggerWrapFactoryLoggerWrap;
		}
		/**
		 * Set {@link LoggerWrap} instance to be used for logging for 
		 * {@link LoggerWrapFactoryImpl} class.
		 * 
		 * @param loggerWrapFactoryLoggerWrap
		 */
		public synchronized static void setLoggerWrapFactoryLoggerWrap( 
				LoggerWrap loggerWrapFactoryLoggerWrap) {
			LoggerWrapFactoryImpl.loggerWrapFactoryLoggerWrap = loggerWrapFactoryLoggerWrap;
		}
	// --------------------------------------------------------------------------------------------
	
	protected LoggerWrapFactoryImpl() {
	}
	
	// LoggerWrapFactoryContract method -----------------------------------------------------------
	public LoggerWrap getLoggerWrap( String loggerName) {
		final String methodName = "getLoggerWrap";
		LoggerWrap loggerWrapFactoryLoggerWrap = LoggerWrapFactoryImpl.getLoggerWrapFactoryLoggerWrap();
		loggerWrapFactoryLoggerWrap.entering(
				methodName, 
				new Object[]{ "loggerName(=" + ((loggerName == null) ? "null" : loggerName) + ")"}
				);

		LoggerWrap loggerWrap = LoggerWrapFactoryImpl.getLoggerWrapInjector().getLoggerWrap();
			loggerWrap.setUpLogger( loggerName);

		loggerWrapFactoryLoggerWrap.exiting( methodName, loggerWrap);
		return loggerWrap;
	}
	// --------------------------------------------------------------------------------------------
	
	public static LoggerWrap getLoggerWrap( 
			String loggerName, LoggerWrapFactory factoryInstance) {
		String methodName = "getLoggerWrap";
		LoggerWrap loggerWrapFactoryLoggerWrap = LoggerWrapFactoryImpl.getLoggerWrapFactoryLoggerWrap();
		loggerWrapFactoryLoggerWrap.entering( 
				methodName, 
				new Object[]{ 
					"loggerName(=" + ((loggerName == null) ? "null" : loggerName) + ")", 
					"factoryInstance(=" + ((factoryInstance == null) 
							? "null" : factoryInstance.toString()) + ")"
					}
				);
		
		LoggerWrapFactory factory = factoryInstance;
			if (factory == null) {
				factory = LoggerWrapFactoryInstanceHolder.getLoggerWrapFactory();
			}
		LoggerWrap loggerWrap = factory.getLoggerWrap( loggerName);
		
		loggerWrapFactoryLoggerWrap.exiting( methodName, loggerWrap.toString());
		return loggerWrap;
	}
	
	
	/**
	 * Convenient method to be used in static method. <br />
	 * By making to get {@link LoggerWrap} instance through this method, it will be easy to
	 * swap <code>LoggerWrapContract</code> instance.
	 * 
	 * @param instance
	 * @param loggerName
	 *            : Ignored when instance input is <code>LoggerWrapContract</code> instance but null.
	 * @param factoryInstance
	 *            : Ignored when instance input is <code>LoggerWrapContract</code> instance but null.
	 *            When instance input is null, this will be used with loggerName input to instantiate 
	 *            <code>LoggerWrapContract</code>  object. When this is null, then one obtained via 
	 *            {@link LoggerWrapFactoryInstanceHolder#getLoggerWrapFactory()} what is an instance 
	 *            of this class will be used as default {@link LoggerWrapFactory} instance.
	 * @return <code>LoggerWrap</code> instance
	 */
	public static LoggerWrap getLoggerWrap(
			LoggerWrap instance, String loggerName, LoggerWrapFactory factoryInstance) {
		String methodName = "getLoggerWrap";
		LoggerWrap loggerWrapFactoryLoggerWrap = LoggerWrapFactoryImpl.getLoggerWrapFactoryLoggerWrap();
		loggerWrapFactoryLoggerWrap.entering( 
				methodName, 
				new Object[]{
						"instance(=" + ((instance == null) ? "null" : instance.toString()) + ")", 
						"loggerName(=" + ((loggerName == null) ? "null" : loggerName) + ")", 
						"factoryInstance(=" + ((factoryInstance == null) ? 
								"null" : factoryInstance.toString()) + ")"
						}
				);
		
		if ( instance != null) {
			loggerWrapFactoryLoggerWrap.exiting( methodName, instance.toString());
			return instance;
		}

		LoggerWrap loggerWrap = LoggerWrapFactoryImpl.getLoggerWrap( loggerName, factoryInstance);
		
		loggerWrapFactoryLoggerWrap.exiting( methodName, loggerWrap.toString());
		return loggerWrap;
	}
	
}
